
#ifndef __BIT_VECTOR_H
#define __BIT_VECTOR_H

#include <apr_atomic.h>

/** Bit vector namespace, supporting atomic operations on bit vectors
 * in some cases using apr_atomic. */
namespace BitVector
{
   /** Gets a bit from the bit vector.
    * 
    * @param data Bit vector data.
    * 
    * @param a Position of the bit to get.
    * 
    * @return Is the bit set? */
   inline bool getBitFast(uint32_t *data, uint64_t a)
   {
      return ((data[a >> 5] & (1U << (a & ((sizeof(uint32_t) * 8) - 1)))) != 0);
   }
   /** Toggle a bit in the bit vector.
    * 
    * @param data Bit vector data.
    * 
    * @param a Position of the bit to toggle. */
   inline void toggleBitFast(uint32_t *data, uint64_t a)
   {
      data[a >> 5] ^= (1U << (a & ((sizeof(uint32_t) * 8) - 1)));
   }
   /** Conditionally set bit.
    * 
    * @param data Bit vector data.
    * 
    * @param cmp Comparison result.
    * 
    * @param a Position of the bit to toggle. */
   inline void setCondBitFast(uint32_t *data, uint32_t cmp, uint64_t a)
   {
      data[a >> 5] ^= ((-(cmp != 0)) ^ (data[a >> 5])) & (1U << (a & ((sizeof(uint32_t) * 8) - 1)));
   }
   /** Set a bit in the bit vector.
    * 
    * @param data Bit vector data.
    * 
    * @param a Position of the bit to set. */
   inline void setBitFast(uint32_t *data, uint64_t a)
   {
      data[a >> 5] |= (1U << (a & ((sizeof(uint32_t) * 8) - 1)));
   }
   /** Unset a bit in the bit vector.
    * 
    * @param data Bit vector data.
    * 
    * @param a Position of the bit to unset. */
   inline void unsetBitFast(uint32_t *data, uint64_t a)
   {
      data[a >> 5] &= ~(1U << (a & ((sizeof(uint32_t) * 8) - 1)));
   }
   /** Conditionally set bit. This operation yields enough information
    * to be threadable and atomic, if the return value is handled
    * properly.
    * 
    * @param data Bit vector data.
    * 
    * @param cmp Comparison result.
    * 
    * @param a Position of the bit to toggle.
    * 
    * @return True if the bit is changed by us, false if someone else
    * changed the bit first, or it was already set in that
    * position. */
   inline bool setCondBitAtomic(uint32_t *data, uint32_t cmp, uint64_t a)
   {
      uint32_t oldvalue = data[a >> 5];
      uint32_t expectedoldvalue = ~oldvalue;
      while (expectedoldvalue != oldvalue)
      {
         expectedoldvalue = oldvalue;
         uint32_t bitflipmask = (1U << (a & ((sizeof(uint32_t) * 8) - 1)));
         if (((oldvalue & bitflipmask) != 0) == (cmp != 0)) return false;
         uint32_t newvalue = oldvalue ^ (((-(cmp != 0)) ^ oldvalue) & bitflipmask);
         oldvalue = apr_atomic_cas32(&(data[a >> 5]), newvalue, expectedoldvalue);
      }
      return true;
   }
   /** Set a bit. This operation yields enough information to be
    * threadable and atomic, if the return value is handled properly.
    * 
    * @param data Bit vector data.
    * 
    * @param a Position of the bit to set.
    * 
    * @return True if the bit is set by us, false if someone else set
    * the bit first, or it was already set. */
   inline bool setBitAtomic(uint32_t *data, uint64_t a)
   {
      uint32_t oldvalue = data[a >> 5];
      uint32_t expectedoldvalue = ~oldvalue;
      while (expectedoldvalue != oldvalue)
      {
         expectedoldvalue = oldvalue;
         uint32_t bitflipmask = (1U << (a & ((sizeof(uint32_t) * 8) - 1)));
         if ((oldvalue & bitflipmask) != 0) return false;
         uint32_t newvalue = oldvalue | bitflipmask;
         oldvalue = apr_atomic_cas32(&(data[a >> 5]), newvalue, expectedoldvalue);
      }
      return true;
   }
   /** Unset a bit. This operation yields enough information to be
    * threadable and atomic, if the return value is handled properly.
    * 
    * @param data Bit vector data.
    * 
    * @param a Position of the bit to unset.
    * 
    * @return True if the bit is unset by us, false if someone else
    * unset the bit first, or it was already unset. */
   inline bool unsetBitAtomic(uint32_t *data, uint64_t a)
   {
      uint32_t oldvalue = data[a >> 5];
      uint32_t expectedoldvalue = ~oldvalue;
      while (expectedoldvalue != oldvalue)
      {
         expectedoldvalue = oldvalue;
         uint32_t bitflipmask = (1U << (a & ((sizeof(uint32_t) * 8) - 1)));
         if ((oldvalue & bitflipmask) == 0) return false;
         uint32_t newvalue = oldvalue & ~bitflipmask;
         oldvalue = apr_atomic_cas32(&(data[a >> 5]), newvalue, expectedoldvalue);
      }
      return true;
   }
}

#endif
